package com.fly.okhttp.util;

import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;

import com.fly.okhttp.OKHttpApplication;
import com.fly.okhttp.exception.OKResponseException;
import com.fly.okhttp.global.Constant;
import com.fly.okhttp.param.OKHttpParams;
import com.squareup.okhttp.Call;
import com.squareup.okhttp.Callback;
import com.squareup.okhttp.FormEncodingBuilder;
import com.squareup.okhttp.Headers;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.MultipartBuilder;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;

/**
 * Created with Android Studio.
 * Package_Name：com.fly.okhttp
 * Project_Name：OKHttpProject
 * User：郭鹏飞
 * Date：2015/11/20
 * Email：love518420@foxmail.com
 * Description：OKHttp工具类单例-对外提供静态方法
 */
public class OKHttpUtil {

    private static OKHttpUtil instance;

    private OkHttpClient okHttpClient;

    private Handler mOKHandler;
    // 存放请求标签 可以用于取消请求
    private ArrayList<Object> tags;

    // 请求是否被取消的标记集合
    private Map<Object, Boolean> tagCancelFlags;

    // 构造函数私有 防止外部通过构造方法实例化
    private OKHttpUtil() {
        // 进行初始化操作
        okHttpClient = new OkHttpClient();
        okHttpClient.setConnectTimeout(Constant.CONNECT_TIME_OUT, TimeUnit.SECONDS);
        okHttpClient.setReadTimeout(Constant.READ_TIME_OUT, TimeUnit.SECONDS);
        mOKHandler = new Handler(Looper.getMainLooper());
        tags = new ArrayList<>();
        tagCancelFlags = new HashMap<>();
    }

    /**
     * 得到当前的操作实例 方法私有 对外提供静态方法访问
     *
     * @return OKHttpClient
     */
    public static OKHttpUtil getInstance() {
        if (instance == null) {
            synchronized (OKHttpUtil.class) {
                if (instance == null) {
                    instance = new OKHttpUtil();
                }
            }
        }
        return instance;
    }

    /**
     * 执行请求 - 异步
     *
     * @param tag      请求标签
     * @param request  请求
     * @param callBack 回调
     */
    private void enqueue(final Object tag, final Request request,
                         final OKHttpResultCallBack callBack) {
        // 进行网络判断
        if (!NetUtil.isNetworkAvailable(OKHttpApplication.getInstance())) {
            sendNoInternetMessage(tag, callBack);
            return;
        }
        // 得到Call对象 并把该对象添加带调度队列中 等待执行
        /*
        * newCall 会得到一个Call对象
        * Call是一个准备好执行的请求 可以取消 表示单个的请求/响应流 不能执行俩次
        * enqueue 添加带调度队列中 等待执行
        * 通常会立即执行，除非当前有正在执行的请求
        * 接收一个回调 在完成或者异常时调用
        * 如果在回调之前执行cancel操作不会回调 （注意回调在当前线程不在UI线程）
        * */
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                sendFailureMessage(tag, e, callBack);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                if (response.isSuccessful()) {
                    sendResponseMessage(tag, response.body().string(), callBack);
                } else {
                    sendFailureMessage(tag, new OKResponseException(), callBack);
                }
            }
        });
    }

    /**
     * 执行请求 - 同步
     *
     * @param tag      请求标签
     * @param request  请求
     * @param callBack 回调 只需要关注 noInternet 这个回调方法即可
     */
    private void execute(final Object tag, final Request request,
                         final OKHttpResultCallBack callBack) {
        // 进行网络判断
        if (!NetUtil.isNetworkAvailable(OKHttpApplication.getInstance())) {
            sendNoInternetMessage(tag, callBack);
            return;
        }
        try {
            Call call = okHttpClient.newCall(request);
            /*
            * execute 会立即执行请求，并会阻塞在当前线程 （所以不要在UI线程里面执行该方法，避免ANR异常）
            * */
            call.execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    ////////////// 对外公布的get方法（异步） /////////////////////////////////////////////

    public static void getAsync(Object tag, String url, OKHttpResultCallBack callBack) {
        getAsync(tag, url, null, callBack);
    }

    public static void getAsync(Object tag, String url,
                                String key, String value, OKHttpResultCallBack callBack) {
        OKHttpParams params = new OKHttpParams(url);
        params.put(key, value);
        getAsync(tag, params.getCompleteUrl(), null, callBack);
    }

    public static void getAsync(String url, OKHttpResultCallBack callBack) {
        getAsync((Object) null, url, null, callBack);
    }

    public static void getAsync(String url,
                                String key, String value, OKHttpResultCallBack callBack) {
        OKHttpParams params = new OKHttpParams(url);
        params.put(key, value);
        getAsync(null, url, params, callBack);
    }

    public static void getAsync(String url, OKHttpParams params,
                                OKHttpResultCallBack callBack) {
        getAsync(null, url, params, callBack);
    }

    /////////////////////    get入口 //////////////////////////////////////////////////
    public static void getAsync(Object tag, String url,
                                OKHttpParams params, OKHttpResultCallBack callBack) {
        getInstance()._get(tag, Constant.THREAD_ASYNC, url, params, callBack);
    }

    ////////////// 对外公布的get方法（同步） /////////////////////////////////////////////

    public static void getSync(Object tag, String url, OKHttpResultCallBack callBack) {
        getSync(tag, url, null, callBack);
    }

    public static void getSync(Object tag, String url,
                               String key, String value, OKHttpResultCallBack callBack) {
        OKHttpParams params = new OKHttpParams(url);
        params.put(key, value);
        getSync(tag, params.getCompleteUrl(), null, callBack);
    }

    public static void getSync(String url, OKHttpResultCallBack callBack) {
        getSync((Object) null, url, null, callBack);
    }

    public static void getSync(String url,
                               String key, String value, OKHttpResultCallBack callBack) {
        OKHttpParams params = new OKHttpParams(url);
        params.put(key, value);
        getSync(null, url, params, callBack);
    }

    public static void getSync(String url, OKHttpParams params,
                               OKHttpResultCallBack callBack) {
        getSync(null, url, params, callBack);
    }

    /////////////////////    get同步入口 //////////////////////////////////////////////////
    public static void getSync(Object tag, String url,
                               OKHttpParams params, OKHttpResultCallBack callBack) {
        getInstance()._get(tag, Constant.THREAD_SYNC, url, params, callBack);
    }

    /**
     * 异步执行get请求
     *
     * @param tag        请求标签可以为空 用于取消请求
     * @param threadType 执行同步请求还是异步请求 {#link Constant}
     * @param url        访问地址
     * @param params     参数可以为空
     * @param callBack   回调
     */
    private void _get(Object tag, int threadType, String url,
                      OKHttpParams params, OKHttpResultCallBack callBack) {
        if (TextUtils.isEmpty(url)) {
            throw new NullPointerException("url为空");
        }
        if (params != null) {
            url = params.getCompleteUrl(url);
        }
        /*
        * 请求辅助类
        * 封装请求地址，请求方法，请求标签，请求头以及请求体
        * */
        Request.Builder builder;
        builder = new Request.Builder().get().url(url);
        if (tag != null) {
            tags.add(tag);// 添加请求标签到集合
            tagCancelFlags.put(tag, false);
            builder.tag(tag);
        }
        if (threadType == Constant.THREAD_ASYNC) {
            enqueue(tag, builder.build(), callBack);
        } else if (threadType == Constant.THREAD_SYNC) {
            execute(tag, builder.build(), callBack);
        } else {
            throw new RuntimeException("threadType 参数值非法");
        }
    }

    ///////////////// 对外公布post方法（异步） /////////////////////////////////////////

    public static void postAsync(Object tag, String url, OKHttpResultCallBack callBack) {
        postAsync(tag, url, null, callBack);
    }

    public static void postAsync(Object tag, String url,
                                 String key, String value, OKHttpResultCallBack callBack) {
        OKHttpParams params = new OKHttpParams();
        params.put(key, value);
        postAsync(tag, url, params, callBack);
    }

    public static void postAsync(String url, OKHttpResultCallBack callBack) {
        postAsync((Object) null, url, null, callBack);
    }

    public static void postAsync(String url,
                                 String key, String value, OKHttpResultCallBack callBack) {
        OKHttpParams params = new OKHttpParams();
        params.put(key, value);
        postAsync(null, url, params, callBack);
    }

    public static void postAsync(String url, OKHttpParams params,
                                 OKHttpResultCallBack callBack) {
        postAsync(null, url, params, callBack);
    }

    /////////////////////    post异步入口 //////////////////////////////////////////////////
    public static void postAsync(Object tag, String url, OKHttpParams params,
                                 OKHttpResultCallBack callBack) {
        getInstance()._post(tag, Constant.THREAD_ASYNC, url, params, callBack);
    }

    ///////////////// 对外公布post方法（同步） /////////////////////////////////////////

    public static void postSync(Object tag, String url, OKHttpResultCallBack callBack) {
        postSync(tag, url, null, callBack);
    }

    public static void postSync(Object tag, String url,
                                String key, String value, OKHttpResultCallBack callBack) {
        OKHttpParams params = new OKHttpParams();
        params.put(key, value);
        postSync(tag, url, params, callBack);
    }

    public static void postSync(String url, OKHttpResultCallBack callBack) {
        postSync((Object) null, url, null, callBack);
    }

    public static void postSync(String url,
                                String key, String value, OKHttpResultCallBack callBack) {
        OKHttpParams params = new OKHttpParams();
        params.put(key, value);
        postSync(null, url, params, callBack);
    }

    public static void postSync(String url, OKHttpParams params,
                                OKHttpResultCallBack callBack) {
        postSync(null, url, params, callBack);
    }

    /////////////////////    post同步入口 //////////////////////////////////////////////////
    public static void postSync(Object tag, String url, OKHttpParams params,
                                OKHttpResultCallBack callBack) {
        getInstance()._post(tag, Constant.THREAD_SYNC, url, params, callBack);
    }

    /**
     * 发送post请求
     *
     * @param tag        请求标签可以为空 用于取消请求
     * @param threadType 执行同步请求还是异步请求 {#link Constant}
     * @param url        访问地址
     * @param params     参数可以为空
     * @param callBack   回调
     */
    private void _post(Object tag, int threadType, String url,
                       OKHttpParams params, OKHttpResultCallBack callBack) {
        if (TextUtils.isEmpty(url)) {
            throw new NullPointerException("url为空");
        }
        FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder();
        if (params != null && params.sizeOfNormal() >= 1) { // 进行非文件参数封装
            for (String key : params.getKeyList()) {
                formEncodingBuilder.add(key, params.getValue(key));
            }
        }
        Request.Builder builder;
        RequestBody body = formEncodingBuilder.build();
        builder = new Request.Builder().post(body).url(url);
        if (tag != null) {
            tags.add(tag); // 添加请求标签到集合
            tagCancelFlags.put(tag, false);
            builder.tag(tag);
        }

        if (threadType == Constant.THREAD_ASYNC) {
            enqueue(tag, builder.build(), callBack);
        } else if (threadType == Constant.THREAD_SYNC) {
            execute(tag, builder.build(), callBack);
        } else {
            throw new RuntimeException("threadType 参数值非法");
        }
    }

    /**
     * post异步提交json数据
     *
     * @param url      访问地址
     * @param json     数据
     * @param callBack 回调
     */
    public static void postAsyncJson(String url, String json,
                                     OKHttpResultCallBack callBack) {
        postSyncJson(null, url, json, callBack);
    }

    /**
     * post异步提交json数据
     *
     * @param tag      请求标签 用于取消请求
     * @param url      访问地址
     * @param json     json数据
     * @param callBack 回调
     */
    public static void postAsyncJson(Object tag, String url, String json,
                                     OKHttpResultCallBack callBack) {
        getInstance()._postJson(tag, Constant.THREAD_ASYNC, url, json, callBack);
    }

    /**
     * post同步提交json数据
     *
     * @param url      访问地址
     * @param json     数据
     * @param callBack 回调
     */
    public static void postSyncJson(String url, String json,
                                    OKHttpResultCallBack callBack) {
        postSyncJson(null, url, json, callBack);
    }

    /**
     * post同步提交json数据
     *
     * @param tag      请求标签 用于取消请求
     * @param url      访问地址
     * @param json     数据
     * @param callBack 回调
     */
    public static void postSyncJson(Object tag, String url, String json,
                                    OKHttpResultCallBack callBack) {
        getInstance()._postJson(tag, Constant.THREAD_SYNC, url, json, callBack);
    }

    /**
     * post提交json数据
     *
     * @param tag        请求标签 用于取消请求
     * @param threadType 异步/同步
     * @param url        访问地址
     * @param json       json数据
     * @param callBack   回调
     */
    private void _postJson(Object tag, int threadType, String url,
                           String json, OKHttpResultCallBack callBack) {
        if (TextUtils.isEmpty(url)) {
            throw new NullPointerException("url为空");
        }
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        RequestBody body = RequestBody.create(JSON, json);
        Request.Builder builder = new Request.Builder().post(body).url(url);
        if (tag != null) {
            tags.add(tag); // 添加请求标签到集合
            tagCancelFlags.put(tag, false);
            builder.tag(tag);
        }

        if (threadType == Constant.THREAD_ASYNC) {
            enqueue(tag, builder.build(), callBack);
        } else if (threadType == Constant.THREAD_SYNC) {
            execute(tag, builder.build(), callBack);
        } else {
            throw new RuntimeException("threadType 参数值非法");
        }
    }

    /////////////// 文件上传对外公布方法 ///////////////////////////////////////////////////

    /////////////////   单个文件上传 ///////////////////////////////////////////////
    public static void uploadFile(Object tag, String url, String mediaType,
                                  String key, File file, OKHttpResultCallBack callBack) {
        if (TextUtils.isEmpty(key)) {
            throw new NullPointerException("key为空");
        }
        if (file == null) {
            throw new NullPointerException("file为空");
        }
        OKHttpParams params = new OKHttpParams();
        params.put(key, file);
        uploadFile(tag, url, mediaType, params, callBack);
    }

    //////////////////////////   多文件同时上传 /////////////////////////////////////
    public static void uploadFile(Object tag, String url, String mediaType,
                                  String key, ArrayList<File> files, OKHttpResultCallBack callBack) {
        if (TextUtils.isEmpty(key)) {
            throw new NullPointerException("key为空");
        }
        if (files == null || files.isEmpty()) {
            throw new NullPointerException("file集合为空");
        }
        OKHttpParams params = new OKHttpParams();
        params.put(key, files);
        uploadFile(tag, url, mediaType, params, callBack);
    }

    public static void uploadFile(String url, String mediaType,
                                  String key, File file, OKHttpResultCallBack callBack) {
        uploadFile(null, url, mediaType, key, file, callBack);
    }

    public static void uploadFile(String url, String mediaType,
                                  String key, ArrayList<File> files, OKHttpResultCallBack callBack) {
        uploadFile(null, url, mediaType, key, files, callBack);
    }

    public static void uploadFile(String url, String mediaType,
                                  OKHttpParams params, OKHttpResultCallBack callBack) {
        uploadFile(null, url, mediaType, params, callBack);
    }

    //////////////////////////   uploadFile 入口  ///////////////////////////////////////
    public static void uploadFile(Object tag, String url, String mediaType,
                                  OKHttpParams params, OKHttpResultCallBack callBack) {
        getInstance()._uploadFile(tag, url, mediaType, params, callBack);
    }

    /////////////// 文件上传对外公布方法 ///////////////////////////////////////////////////

    /**
     * 上传文件 支持多文件上传 (基于post)
     *
     * @param tag       请求标签 用于取消请求
     * @param url       请求地址
     * @param mediaType 文件类型
     * @param params    参数
     * @param callBack  回调
     */
    private void _uploadFile(Object tag, String url, String mediaType,
                             OKHttpParams params, OKHttpResultCallBack callBack) {
        if (TextUtils.isEmpty(url)) {
            throw new NullPointerException("url为空");
        }
        if (TextUtils.isEmpty(mediaType)) {
            throw new NullPointerException("没有指定文件类型");
        }
        if (params == null || params.sizeOfFile() == 0) {
            throw new NullPointerException("文件参数为空");
        }
        MultipartBuilder mulBuilder = new MultipartBuilder().type(MultipartBuilder.FORM);
        // 进行非文件参数封装
        if (params.sizeOfNormal() >= 1) {
            for (String key : params.getKeyList()) {
                mulBuilder.addFormDataPart(key, params.getValue(key));
            }
        }
        // 进行文件参数的封装
        if (params.sizeOfFile() >= 1) {
            for (String key : params.getFileKeys()) {
                ArrayList<File> files = params.getFile(key);
                for (File file : files) {
                    // 注意 必须指定 filename 这个参数
                    mulBuilder.addPart(Headers.of("Content-Disposition", "form-data; name=\""
                                    + key + "\"; filename=\"" + file.getName() + "\""),
                            RequestBody.create(MediaType.parse(mediaType), file));
                }
            }
        }
        // 请求体
        RequestBody body = mulBuilder.build();
        Request.Builder builder = new Request.Builder().post(body).url(url);
        if (tag != null) {
            tags.add(tag); // 添加请求标签到集合
            tagCancelFlags.put(tag, false);
            builder.tag(tag);
        }

        enqueue(tag, builder.build(), callBack);
    }

    /////////////////////// 基于 post 请求的文件下载 ///////////////////////////////////////

    public static void downLoadFilePostAsync(String url, String fileDir,
                                             String fileName, OKHttpResultCallBack callBack) {
        downLoadFilePostAsync(null, url, null, fileDir, fileName, callBack);
    }

    public static void downLoadFilePostAsync(Object tag, String url, String fileDir,
                                             String fileName, OKHttpResultCallBack callBack) {
        downLoadFilePostAsync(tag, url, null, fileDir, fileName, callBack);
    }

    public static void downLoadFilePostAsync(String url, OKHttpParams params, String fileDir,
                                             String fileName, OKHttpResultCallBack callBack) {
        downLoadFilePostAsync(null, url, params, fileDir, fileName, callBack);
    }

    public static void downLoadFilePostAsync(Object tag, String url, OKHttpParams params,
                                             String fileDir, String fileName, OKHttpResultCallBack callBack) {
        getInstance()._downLoadFilePostAsync(tag, url, params, fileDir, fileName, callBack);
    }

    /////////////////////// 基于 post 请求的文件下载 ///////////////////////////////////////

    /**
     * 基于post下载文件
     *
     * @param tag      请求标签
     * @param url      地址
     * @param params   参数
     * @param fileDir  文件目录
     * @param fileName 文件名称
     * @param callBack 回调
     */
    private void _downLoadFilePostAsync(final Object tag, String url, OKHttpParams params,
                                        final String fileDir, final String fileName, final OKHttpResultCallBack callBack) {
        if (TextUtils.isEmpty(url)) {
            throw new NullPointerException("url为空");
        }
        if (TextUtils.isEmpty(fileDir)) {
            throw new NullPointerException("文件目录为空");
        }
        if (TextUtils.isEmpty(fileName)) {
            throw new NullPointerException("文件名为空");
        }
        if (!NetUtil.isNetworkAvailable(OKHttpApplication.getInstance())) {
            sendNoInternetMessage(tag, callBack);
            return;
        }
        // 封装参数
        FormEncodingBuilder formEncodingBuilder = new FormEncodingBuilder();
        if (params != null && params.sizeOfNormal() >= 1) {
            for (String key : params.getKeyList()) {
                formEncodingBuilder.add(key, params.getValue(key));
            }
        }
        RequestBody body = formEncodingBuilder.build();
        Request.Builder builder = new Request.Builder().post(body).url(url);
        if (tag != null) {
            tags.add(tag);
            tagCancelFlags.put(tag, false);
            builder.tag(tag);
        }
        Request request = builder.build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                sendFailureMessage(tag, e, callBack);
            }

            @Override
            public void onResponse(Response response) {
                parseFile(response, fileDir, fileName, tag, callBack);
            }
        });
    }

    ////////////// 基于 get 请求的文件下载 ///////////////////////////////////////////////

    public static void downLoadFileGetAsync(String url, String fileDir,
                                            String fileName, OKHttpResultCallBack callBack) {
        downLoadFileGetAsync(null, url, null, fileDir, fileName, callBack);
    }

    public static void downLoadFileGetAsync(Object tag, String url, String fileDir,
                                            String fileName, OKHttpResultCallBack callBack) {
        downLoadFileGetAsync(tag, url, null, fileDir, fileName, callBack);
    }

    public static void downLoadFileGetAsync(String url, OKHttpParams params, String fileDir,
                                            String fileName, OKHttpResultCallBack callBack) {
        downLoadFileGetAsync(null, url, params, fileDir, fileName, callBack);
    }

    public static void downLoadFileGetAsync(Object tag, String url, OKHttpParams params,
                                            String fileDir, String fileName, OKHttpResultCallBack callBack) {
        getInstance()._downLoadFileGetAsync(tag, url, params, fileDir, fileName, callBack);
    }

    ////////////// 基于 get 请求的文件下载 ///////////////////////////////////////////////

    /**
     * 基于 get 下载文件
     *
     * @param tag      请求标签
     * @param url      地址
     * @param params   参数
     * @param fileDir  文件目录
     * @param fileName 文件名
     */
    private void _downLoadFileGetAsync(final Object tag, String url, OKHttpParams params,
                                       final String fileDir, final String fileName, final OKHttpResultCallBack callBack) {
        if (TextUtils.isEmpty(url)) {
            throw new NullPointerException("url为空");
        }
        if (TextUtils.isEmpty(fileDir)) {
            throw new NullPointerException("文件目录为空");
        }
        if (TextUtils.isEmpty(fileName)) {
            throw new NullPointerException("文件名为空");
        }
        if (!NetUtil.isNetworkAvailable(OKHttpApplication.getInstance())) {
            sendNoInternetMessage(tag, callBack);
            return;
        }
        if (params != null && params.sizeOfNormal() >= 1) { // 存在参数进行地址拼接
            url = params.getCompleteUrl(url);
        }
        Request.Builder builder = new Request.Builder().get().url(url);
        if (tag != null) {
            tags.add(tag);
            tagCancelFlags.put(tag, false);
            builder.tag(tag);
        }
        okHttpClient.newCall(builder.build()).enqueue(new Callback() {
            @Override
            public void onFailure(Request request, IOException e) {
                sendFailureMessage(tag, e, callBack);
            }

            @Override
            public void onResponse(Response response) throws IOException {
                parseFile(response, fileDir, fileName, tag, callBack);
            }
        });
    }

    /**
     * 从响应流里面读取文件数据
     *
     * @param response 响应
     * @param fileDir  文件目录
     * @param fileName 文件名
     * @param tag      请求标签
     * @param callBack 回调
     */
    private void parseFile(Response response, String fileDir, String fileName, Object tag, OKHttpResultCallBack callBack) {
        if (response.isSuccessful()) {
            // 读取文件
            InputStream is = null;
            byte[] buffer = new byte[1024];
            int len;
            long currentTotalLen = 0L;
            FileOutputStream fos = null;
            try {
                is = response.body().byteStream();
                File file = new File(fileDir, fileName);
                if (file.exists()) // 如果文件存在 则删除
                    file.delete();
                fos = new FileOutputStream(file);
                while ((len = is.read(buffer)) != -1) {
                    fos.write(buffer, 0, len);
                    currentTotalLen += len;
                    sendDownProgressMessage(tag, len, currentTotalLen,
                            response.body().contentLength(), callBack);
                }
                fos.flush();
                sendResponseMessage(tag, file.getAbsolutePath(), callBack);
            } catch (IOException e) {
                e.printStackTrace();
                sendFailureMessage(tag, e, callBack);
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (fos != null) {
                            try {
                                fos.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        } else {
            sendFailureMessage(tag, new OKResponseException(), callBack);
        }
    }

    /**
     * 取消请求
     *
     * @param tag 请求标签
     */
    private void _cancelRequest(Object tag, OnCancelRequestHandler cancelRequestHandler) {
        if (tag == null || !tags.contains(tag)) {
            throw new NullPointerException("tag为空或不存在");
        }
        tagCancelFlags.put(tag, true); // 设置取消标记
        okHttpClient.cancel(tag); // 取消请求
        tags.remove(tag); // 将tag从请求标签集合里面移除
        // 添加一个回调
        if (cancelRequestHandler != null) {
            cancelRequestHandler.onCancle();
        }
    }

    /**
     * 取消全部请求
     */
    private void _cancelAll(OnCancelRequestHandler cancelRequestHandler) {
        if (tags == null || tags.isEmpty()) {
            return;
        }
        for (Object tag : getInstance().tags) {
            okHttpClient.cancel(tag);
            tagCancelFlags.put(tag, true);
        }
        tags.clear();
        if (cancelRequestHandler != null) {
            cancelRequestHandler.onCancle();
        }
    }

    ////////////////////////////// 对外公布的取消请求的方法 //////////////////////////////

    public static void cancelRequest(Object tag, OnCancelRequestHandler cancelRequestHandler) {
        getInstance()._cancelRequest(tag, cancelRequestHandler);
    }

    public static void cancelAll(OnCancelRequestHandler cancelRequestHandler) {
        getInstance()._cancelAll(cancelRequestHandler);
    }

    /**
     * 获取OkHttpClient对象
     *
     * @return OkHttpClient
     */
    public OkHttpClient getOkHttpClient() {
        return okHttpClient;
    }

    /**
     * 进行证书验证 访问https
     *
     * @param certificates 文件流
     */
    private void _setCertificates(InputStream... certificates) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            for (InputStream certificate : certificates) {
                String certificateAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));

                try {
                    if (certificate != null)
                        certificate.close();
                } catch (IOException ignored) {
                }
            }

            SSLContext sslContext = SSLContext.getInstance("TLS");

            TrustManagerFactory trustManagerFactory =
                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

            trustManagerFactory.init(keyStore);
            sslContext.init
                    (
                            null,
                            trustManagerFactory.getTrustManagers(),
                            new SecureRandom()
                    );

            okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /////////////////////  对外公布的 设置安全证书的方法 /////////////////////////////
    public static void setCertificates(InputStream... certificates) {
        getInstance()._setCertificates(certificates);
    }

    /**
     * 发送一个没有网络的消息
     *
     * @param tag      请求标签
     * @param callBack 回调
     */
    private void sendNoInternetMessage(Object tag, final OKHttpResultCallBack callBack) {
        if (tag != null && tags.contains(tag)) {
            tags.remove(tag); // 移除请求标签
        }
        mOKHandler.post(new Runnable() { // 回到UI线程
            @Override
            public void run() {
                if (callBack != null) {
                    callBack.noInternet();
                }
            }
        });
    }

    /**
     * 发送一个请求失败的消息
     *
     * @param tag      请求标签
     * @param callBack 回调
     */
    private void sendFailureMessage(Object tag, final Exception e, final OKHttpResultCallBack callBack) {
        if (tag != null && tags.contains(tag)) {
            tags.remove(tag); // 移除请求标签
        }
        if (isCancel(tag)) { // 该请求被取消不在回调这个方法
            tagCancelFlags.remove(tag);
            return;
        }
        mOKHandler.post(new Runnable() { // 回到UI线程
            @Override
            public void run() {
                if (callBack != null) {
                    callBack.onFailure(e);
                }
            }
        });
    }

    /**
     * 发送一个请求成功的消息
     *
     * @param tag      请求标签
     * @param result   成功的响应
     * @param callBack 回调
     */
    private void sendResponseMessage(Object tag, final String result,
                                     final OKHttpResultCallBack callBack) {
        if (tag != null && tags.contains(tag)) {
            tags.remove(tag); // 移除请求标签
        }
        if (isCancel(tag)) { // 该请求被取消不在回调这个方法
            tagCancelFlags.remove(tag);
            return;
        }
        mOKHandler.post(new Runnable() {
            @Override
            public void run() {
                if (callBack != null) {
                    callBack.onResponse(result);
                }
            }
        });
    }

    /**
     * 发送一个请求失败的消息
     *
     * @param callBack 回调
     */
    private void sendDownProgressMessage(Object tag, final int len, final long currentTotalLen,
                                         final long total, final OKHttpResultCallBack callBack) {
        if (isCancel(tag)) {
            tagCancelFlags.remove(tag);
            return;
        }
        mOKHandler.post(new Runnable() { // 回到UI线程
            @Override
            public void run() {
                if (callBack != null) {
                    callBack.onDownProgress(len, currentTotalLen, total);
                }
            }
        });
    }

    /**
     * 判断请求是否取消
     *
     * @param tag 请求标签
     * @return true请求取消 false请求没有取消
     */
    private boolean isCancel(Object tag) {
        return tag != null && tagCancelFlags.containsKey(tag)
                && tagCancelFlags.get(tag);
    }

    public interface OKHttpResultCallBack {
        // 没有网络
        void noInternet();

        // 失败
        void onFailure(Exception e);

        // 响应
        void onResponse(String result);

        /**
         * 下载进度回调
         *
         * @param len             每次回调的写入长度
         * @param currentTotalLen 当前写入的总长度
         * @param total           文件总长度 当文件总长度未知时返回-1
         */
        void onDownProgress(int len, long currentTotalLen, long total);
    }

    public interface OnCancelRequestHandler {
        // 取消
        void onCancle();
    }

}
